This notebook defines the most focal recurrent copy number units by removing focal changes that are within entire chromosome arm losses and gains. Most focal here meaning:
- If a chromosome arm is not clearly defined as a gain or loss (and is callable) we look to define the cytoband level status
- If a cytoband is not clearly defined as a gain or loss (and is callable) we then look to define the gene-level status
Usage
This notebook is intended to be run from the command line with the following (assumes you are in the root directory of the repository):
Rscript -e "rmarkdown::render('analyses/focal-cn-file-preparation/05-define-most-focal-cn-units.Rmd', clean = TRUE)"
Cutoffs:
# The percentage of calls a particular status needs to be
# above to be called the majority status -- the decision
# for a cutoff of 90% here was made to ensure that the status
# is not only the majority status but it is also significantly
# called more than the other status values in the region
percent_threshold <- 0.9
# The percentage threshold for determining if enough of a region
# (arm, cytoband, or gene) is callable to determine its status --
# the decision for a cutoff of 50% here was made as it seems reasonable
# to expect a region to be more than 50% callable for a dominant status
# call to be made
uncallable_threshold <- 0.5
Set up
Libraries and functions
library(tidyverse)
Custom Function
status_majority_caller <- function(status_df,
region_variable,
status_column_name,
percent_threshold_value,
uncallable_threshold_value = uncallable_threshold) {
# Given a data.frame with cytoband/gene-level copy number status data,
# find the dominant status of the cytoband/gene region by calculating
# the percentages of the region that each call represents.
#
# Args:
# status_df: data.frame with cytoband/gene-level copy number status data
# region_variable: string of the column name/region to calculate copy number
# status percentages for
# status_column_name: string of the column name that holds the relevant copy
# number status data
# percent_threshold_value: What percent of calls a particular status needs to be
# above to be called the majority.
# uncallable_threshold_value: a threshold for determining if enough of a region is
# callable to determine its status.
#
# Return:
# status_count: data.frame with percentage values for each unique status in
# each unique region (arm/cytoband/gene) and the dominant
# status for that region
# Tidyeval for these columns
region_sym <- rlang::sym(region_variable)
status_sym <- rlang::sym(status_column_name)
# Format the data and group it
status_count <- status_df %>%
count(!!region_sym, !!status_sym) %>%
# Spread the data -- each row represents a unique chromosome arm
spread(!!status_sym, n) %>%
# Turn NAs into 0s
replace_na(list(
gain = 0,
loss = 0,
neutral = 0,
amplification = 0,
uncallable = 0,
unstable = 0
)) %>%
# Getting counts by region
group_by(!!region_sym)
# Let's store the regions separately so as to avoid weird coercions
region_vector <-
status_count %>%
dplyr::pull(!!region_sym)
# Calculate percent
status_count <- status_count %>%
# Store region variable column out of the way as rownames
tibble::column_to_rownames(region_variable) %>%
# Obtain a total counts variable column
dplyr::mutate(total = apply(., 1, sum)) %>%
# Get the ratio of each status count to the total
dplyr::mutate_at(dplyr::vars(-total), dplyr::funs(. / total)) %>%
# Bring back our region variable as its own column
dplyr::mutate(!!region_variable := region_vector)
# The logic here is to define the region status based on the majority of calls
# in the region -- if the number of calls for a specific status is
# responsible for more than `percent_threshold` value of the total calls in that
# region, then it is used to define the region's status (exception is for the
# uncallable status where we define the region as uncallable when more than the
# `uncallable_threshold` of the calls in that region are uncallable)
if ((region_variable == "chromosome_arm") | (region_variable == "gene_symbol")) {
status_count <- status_count %>%
mutate(
dominant_status = case_when(
gain > percent_threshold ~ "gain",
loss > percent_threshold ~ "loss",
amplification > percent_threshold ~ "amplification",
TRUE ~ "neutral"
)
)
} else if (region_variable == "cytoband") {
status_count <- status_count %>%
mutate(
dominant_status = case_when(
uncallable > uncallable_threshold ~ "uncallable",
gain > percent_threshold~ "gain",
loss > percent_threshold ~ "loss",
neutral > percent_threshold ~ "neutral",
TRUE ~ "unstable"
)
)
}
return(status_count)
}
plot_dominant_status_calls <- function(status_count_df,
region_variable) {
# Given a data.frame with the percentage values for each region and the
# dominant status for that region, plot the dominant status call on the
# x-axis with the percentage values on the y-axis.
#
# Args:
# status_count_df: data.frame with percentage values for each unique status
# in each unique region (arm/cytoband/gene) and the
# dominant status for that region
# region_variable: string of the region (which will also be a column name)
# that the data.frame holds percentage values for
# Return:
# status_plot: plot representing the dominant status call on the x-axis and
# the percentage values on the y-axis
status_count_df %>%
# Remove the total column -- we don't want to plot this
select(-total) %>%
dplyr::ungroup() %>%
# Store the non-percentage value column variable as rownames
tibble::column_to_rownames(region_variable) %>%
# Gather the data.frame to have columns and values in the format of
# our status call, the percentage of total calls that status call is
# responisble for, and the dominant status call made based on the
# percentage value
tidyr::gather(status, percent,-dominant_status) %>%
# Plot our focal status values on the x-axis and the percentage values
# on the y-axis
ggplot2::ggplot(ggplot2::aes(x = status,
y = percent)) +
ggplot2::geom_jitter() +
# Facet wrap around each dominant status value
ggplot2::facet_wrap( ~ dominant_status)
}
Files and directories
results_dir <- "results"
# Define a logical object for running in CI
running_in_ci <- params$is_ci
Read in files
Read in cytoband status file and format it for what we will need in this notebook.
# Read in the file with consensus CN status data and the UCSC cytoband data --
# generated in `03-add-cytoband-status-consensus.Rmd`
consensus_seg_cytoband_status_df <-
read_tsv(file.path("results", "consensus_seg_with_ucsc_cytoband_status.tsv.gz")) %>%
# Need this to not have `chr`
mutate(chr = gsub("chr", "", chr),
cytoband = paste0(chr, cytoband)) %>%
select(
chromosome_arm,
# Distinguish this dominant status that is based on cytobands, from the status
dominant_cytoband_status = dominant_status,
cytoband,
Kids_First_Biospecimen_ID
)
Parsed with column specification:
cols(
Kids_First_Biospecimen_ID = [31mcol_character()[39m,
chr = [31mcol_character()[39m,
cytoband = [31mcol_character()[39m,
dominant_status = [31mcol_character()[39m,
band_length = [32mcol_double()[39m,
callable_fraction = [32mcol_double()[39m,
gain_fraction = [32mcol_double()[39m,
loss_fraction = [32mcol_double()[39m,
chromosome_arm = [31mcol_character()[39m
)
Read in the chromosome-level and gene-level data.
# Read in the annotated CN file (without the UCSC data)
consensus_seg_autosomes_df <-
read_tsv(file.path(results_dir, "consensus_seg_annotated_cn_autosomes.tsv.gz"))
Parsed with column specification:
cols(
biospecimen_id = [31mcol_character()[39m,
status = [31mcol_character()[39m,
copy_number = [32mcol_double()[39m,
ploidy = [32mcol_double()[39m,
ensembl = [31mcol_character()[39m,
gene_symbol = [31mcol_character()[39m,
cytoband = [31mcol_character()[39m
)
Joining the gene-level, cytoband-level, and arm-level data into one data frame.
combined_status_df <- consensus_seg_autosomes_df %>%
inner_join(
consensus_seg_cytoband_status_df,
by = c("biospecimen_id" = "Kids_First_Biospecimen_ID", "cytoband")
)
Define most focal units
Determine chromosome arm status
# Use our custom function to make the status calls
arm_status_count <- status_majority_caller(
combined_status_df,
"chromosome_arm",
"status",
percent_threshold = percent_threshold
)
# Display table
arm_status_count %>%
group_by(dominant_status)
These are the chromosome arms that have not been defined as gain or loss – we want to define their cytoband/gene-level status
# Let's get a vector of the neutral arms
neutral_arms <- arm_status_count %>%
filter(dominant_status == "neutral") %>%
dplyr::pull("chromosome_arm")
Determine cytoband status
We want to include cytoband and gene-level calls for chromosome arms that have not been defined as a gain or loss.
# Filter the annotated CN data to include only neutral chromosome arms
neutral_status_arm_df <- combined_status_df %>%
filter(chromosome_arm %in% neutral_arms)
Making cytoband-level majority calls.
if (!(running_in_ci)) {
# Now count the cytoband level calls (for each status call) and define
# the cytoband as that status if more than 50% of the total counts are
# for that particular status
cytoband_status_count <- status_majority_caller(
neutral_status_arm_df,
"cytoband",
"dominant_cytoband_status",
percent_threshold = percent_threshold
)
# Display table
cytoband_status_count
}
Determine gene-level status
if (!(running_in_ci)) {
# These are the cytobands that have not been defined as gain or loss --
# we want to define their gene-level status
neutral_cytobands <- cytoband_status_count %>%
filter(dominant_status %in% c("unstable", "neutral")) %>%
dplyr::pull("cytoband")
# Filter the annotated CN data to include only these cytobands
neutral_status_cytoband_df <- combined_status_df %>%
filter(cytoband %in% neutral_cytobands)
# Now count the gene-level calls (for each status call) and define
# the gene as that status if more than 50% of the total counts are
# for that particular status
gene_status_count <- status_majority_caller(neutral_status_cytoband_df,
"gene_symbol",
"status",
percent_threshold = percent_threshold)
# Display table
gene_status_count
}
Plot calls
Plot the final dominant status call on the x-axis and the percent of each status on the y-axis.
Plot chromosome arm status calls
# Run `plot_dominant_status_calls` function for the chromosome arm calls
plot_dominant_status_calls(
arm_status_count,
"chromosome_arm"
)

Plot cytoband status calls
# Run `plot_dominant_status_calls` function for the cytoband calls if not
# running in CI
if (!(running_in_ci)) {
plot_dominant_status_calls(cytoband_status_count,
"cytoband")
}

Plot gene status calls
# Plot if not running in circleCI
if (!(running_in_ci)) {
plot_dominant_status_calls(gene_status_count,
"gene_symbol")
}

Combine arm, cytoband, and gene-level status data
# The logic variable `running_in_ci` is needed here because the CI testing
# files do not contain any of the genes in the `gene_status_count` data.frame
# generated above (when `running_in_ci` == FALSE)
if (!(running_in_ci)) {
final_df <- consensus_seg_autosomes_df %>%
mutate(chromosome_arm = gsub("(p|q).*", "\\1", cytoband)) %>%
inner_join(arm_status_count,
by = "chromosome_arm") %>%
left_join(cytoband_status_count,
by = "cytoband",
suffix = c(".arm", ".cytoband")) %>%
left_join(gene_status_count,
by = "gene_symbol",
suffix = c(".arm", ".gene")) %>%
mutate(
focal_call = paste0(
dominant_status.arm,
", ",
dominant_status.cytoband,
", ",
dominant_status
),
# Here we want to define the most focal call based on the arm, cytoband,
# and gene status information -- if a loss/gain is defined at the arm
# level then the focal call will be "arm_loss" or "arm_gain" respectively,
# and so on.
focal_call = case_when(focal_call == "loss, NA, NA" ~ "arm_loss",
focal_call == "neutral, uncallable, NA" ~ "uncallable",
focal_call == "neutral, loss, NA" ~ "cytoband_loss",
focal_call == "neutral, NA, NA" ~ "arm_neutral",
focal_call == "neutral, unstable, neutral" ~ "gene_neutral",
focal_call == "neutral, unstable, loss" ~ "gene_loss",
TRUE ~ "Other")
)
} else {
final_df <- consensus_seg_autosomes_df %>%
mutate(chromosome_arm = gsub("(p|q).*", "\\1", cytoband)) %>%
inner_join(arm_status_count, by = "chromosome_arm") %>%
mutate(focal_call = dominant_status)
}
# Display final table
final_df %>%
arrange(focal_call) %>%
group_by(biospecimen_id, focal_call)
Session Info
sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 9 (stretch)
Matrix products: default
BLAS/LAPACK: /usr/lib/libopenblasp-r0.2.19.so
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8
[4] LC_COLLATE=en_US.UTF-8 LC_MONETARY=en_US.UTF-8 LC_MESSAGES=C
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C LC_ADDRESS=C
[10] LC_TELEPHONE=C LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] patchwork_1.0.0.9000 forcats_0.4.0 stringr_1.4.0 dplyr_0.8.3
[5] purrr_0.3.2 readr_1.3.1 tidyr_0.8.3 tibble_2.1.3
[9] ggplot2_3.2.0 tidyverse_1.2.1
loaded via a namespace (and not attached):
[1] Rcpp_1.0.1 cellranger_1.1.0 pillar_1.4.2 compiler_3.6.0 base64enc_0.1-3 tools_3.6.0
[7] zeallot_0.1.0 digest_0.6.20 evaluate_0.14 jsonlite_1.6 lubridate_1.7.4 nlme_3.1-140
[13] gtable_0.3.0 lattice_0.20-38 pkgconfig_2.0.2 rlang_0.4.0 cli_1.1.0 rstudioapi_0.10
[19] yaml_2.2.0 haven_2.1.1 xfun_0.8 withr_2.1.2 xml2_1.2.0 httr_1.4.0
[25] knitr_1.23 vctrs_0.1.0 generics_0.0.2 hms_0.4.2 grid_3.6.0 tidyselect_0.2.5
[31] glue_1.3.1 R6_2.4.0 fansi_0.4.0 readxl_1.3.1 rmarkdown_1.13 modelr_0.1.4
[37] magrittr_1.5 backports_1.1.4 scales_1.0.0 htmltools_0.3.6 rsconnect_0.8.13 rvest_0.3.4
[43] assertthat_0.2.1 colorspace_1.4-1 labeling_0.3 utf8_1.1.4 stringi_1.4.3 lazyeval_0.2.2
[49] munsell_0.5.0 broom_0.5.2 crayon_1.3.4
LS0tCnRpdGxlOiAiRmluZCBtb3N0IGZvY2FsIHJlY3VycmVudCBjb3B5IG51bWJlciB1bml0cyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKYXV0aG9yOiBDaGFudGUgQmV0aGVsbCBmb3IgQUxTRiBDQ0RMCmRhdGU6IDIwMjAKcGFyYW1zOgogIGlzX2NpOiBGQUxTRQotLS0KClRoaXMgbm90ZWJvb2sgZGVmaW5lcyB0aGUgbW9zdCBmb2NhbCByZWN1cnJlbnQgY29weSBudW1iZXIgdW5pdHMgYnkgcmVtb3ZpbmcgZm9jYWwgY2hhbmdlcyB0aGF0IGFyZSB3aXRoaW4gZW50aXJlIGNocm9tb3NvbWUgYXJtIGxvc3NlcyBhbmQgZ2FpbnMuCl9Nb3N0IGZvY2FsXyBoZXJlIG1lYW5pbmc6CgotIElmIGEgY2hyb21vc29tZSBhcm0gaXMgbm90IGNsZWFybHkgZGVmaW5lZCBhcyBhIGdhaW4gb3IgbG9zcyAoYW5kIGlzIGNhbGxhYmxlKSB3ZSBsb29rIHRvIGRlZmluZSB0aGUgY3l0b2JhbmQgbGV2ZWwgc3RhdHVzCi0gSWYgYSBjeXRvYmFuZCBpcyBub3QgY2xlYXJseSBkZWZpbmVkIGFzIGEgZ2FpbiBvciBsb3NzIChhbmQgaXMgY2FsbGFibGUpIHdlIHRoZW4gbG9vayB0byBkZWZpbmUgdGhlIGdlbmUtbGV2ZWwgc3RhdHVzCgojIyBVc2FnZQoKVGhpcyBub3RlYm9vayBpcyBpbnRlbmRlZCB0byBiZSBydW4gZnJvbSB0aGUgY29tbWFuZCBsaW5lIHdpdGggdGhlIGZvbGxvd2luZyAoYXNzdW1lcyB5b3UgYXJlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGUgcmVwb3NpdG9yeSk6CgpgYGAKUnNjcmlwdCAtZSAicm1hcmtkb3duOjpyZW5kZXIoJ2FuYWx5c2VzL2ZvY2FsLWNuLWZpbGUtcHJlcGFyYXRpb24vMDUtZGVmaW5lLW1vc3QtZm9jYWwtY24tdW5pdHMuUm1kJywgY2xlYW4gPSBUUlVFKSIKYGBgCgojIyMgQ3V0b2ZmczogCgpgYGB7cn0KIyBUaGUgcGVyY2VudGFnZSBvZiBjYWxscyBhIHBhcnRpY3VsYXIgc3RhdHVzIG5lZWRzIHRvIGJlIAojIGFib3ZlIHRvIGJlIGNhbGxlZCB0aGUgbWFqb3JpdHkgc3RhdHVzIC0tIHRoZSBkZWNpc2lvbgojIGZvciBhIGN1dG9mZiBvZiA5MCUgaGVyZSB3YXMgbWFkZSB0byBlbnN1cmUgdGhhdCB0aGUgc3RhdHVzCiMgaXMgbm90IG9ubHkgdGhlIG1ham9yaXR5IHN0YXR1cyBidXQgaXQgaXMgYWxzbyBzaWduaWZpY2FudGx5CiMgY2FsbGVkIG1vcmUgdGhhbiB0aGUgb3RoZXIgc3RhdHVzIHZhbHVlcyBpbiB0aGUgcmVnaW9uCnBlcmNlbnRfdGhyZXNob2xkIDwtIDAuOQoKIyBUaGUgcGVyY2VudGFnZSB0aHJlc2hvbGQgZm9yIGRldGVybWluaW5nIGlmIGVub3VnaCBvZiBhIHJlZ2lvbgojIChhcm0sIGN5dG9iYW5kLCBvciBnZW5lKSBpcyBjYWxsYWJsZSB0byBkZXRlcm1pbmUgaXRzIHN0YXR1cyAtLQojIHRoZSBkZWNpc2lvbiBmb3IgYSBjdXRvZmYgb2YgNTAlIGhlcmUgd2FzIG1hZGUgYXMgaXQgc2VlbXMgcmVhc29uYWJsZQojIHRvIGV4cGVjdCBhIHJlZ2lvbiB0byBiZSBtb3JlIHRoYW4gNTAlIGNhbGxhYmxlIGZvciBhIGRvbWluYW50IHN0YXR1cwojIGNhbGwgdG8gYmUgbWFkZQp1bmNhbGxhYmxlX3RocmVzaG9sZCA8LSAwLjUKYGBgCgojIyBTZXQgdXAKCiMjIyBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIyBDdXN0b20gRnVuY3Rpb24KCmBgYHtyfQpzdGF0dXNfbWFqb3JpdHlfY2FsbGVyIDwtIGZ1bmN0aW9uKHN0YXR1c19kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWdpb25fdmFyaWFibGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHVzX2NvbHVtbl9uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBlcmNlbnRfdGhyZXNob2xkX3ZhbHVlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bmNhbGxhYmxlX3RocmVzaG9sZF92YWx1ZSA9IHVuY2FsbGFibGVfdGhyZXNob2xkKSB7CiAgIyBHaXZlbiBhIGRhdGEuZnJhbWUgd2l0aCBjeXRvYmFuZC9nZW5lLWxldmVsIGNvcHkgbnVtYmVyIHN0YXR1cyBkYXRhLAogICMgZmluZCB0aGUgZG9taW5hbnQgc3RhdHVzIG9mIHRoZSBjeXRvYmFuZC9nZW5lIHJlZ2lvbiBieSBjYWxjdWxhdGluZwogICMgdGhlIHBlcmNlbnRhZ2VzIG9mIHRoZSByZWdpb24gdGhhdCBlYWNoIGNhbGwgcmVwcmVzZW50cy4KICAjCiAgIyBBcmdzOgogICMgICBzdGF0dXNfZGY6IGRhdGEuZnJhbWUgd2l0aCBjeXRvYmFuZC9nZW5lLWxldmVsIGNvcHkgbnVtYmVyIHN0YXR1cyBkYXRhCiAgIyAgIHJlZ2lvbl92YXJpYWJsZTogc3RyaW5nIG9mIHRoZSBjb2x1bW4gbmFtZS9yZWdpb24gdG8gY2FsY3VsYXRlIGNvcHkgbnVtYmVyCiAgIyAgICAgICAgICAgICAgICAgICAgc3RhdHVzIHBlcmNlbnRhZ2VzIGZvcgogICMgICBzdGF0dXNfY29sdW1uX25hbWU6IHN0cmluZyBvZiB0aGUgY29sdW1uIG5hbWUgdGhhdCBob2xkcyB0aGUgcmVsZXZhbnQgY29weQogICMgICAgICAgICAgICAgICAgICAgICAgIG51bWJlciBzdGF0dXMgZGF0YQogICMgICBwZXJjZW50X3RocmVzaG9sZF92YWx1ZTogV2hhdCBwZXJjZW50IG9mIGNhbGxzIGEgcGFydGljdWxhciBzdGF0dXMgbmVlZHMgdG8gYmUgCiAgIyAgICAgICAgICAgICAgICAgICAgICBhYm92ZSB0byBiZSBjYWxsZWQgdGhlIG1ham9yaXR5LiAKICAjICAgdW5jYWxsYWJsZV90aHJlc2hvbGRfdmFsdWU6IGEgdGhyZXNob2xkIGZvciBkZXRlcm1pbmluZyBpZiBlbm91Z2ggb2YgYSByZWdpb24gaXMgCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICBjYWxsYWJsZSB0byBkZXRlcm1pbmUgaXRzIHN0YXR1cy4gCiAgIyAgIAogICMgUmV0dXJuOgogICMgICBzdGF0dXNfY291bnQ6IGRhdGEuZnJhbWUgd2l0aCBwZXJjZW50YWdlIHZhbHVlcyBmb3IgZWFjaCB1bmlxdWUgc3RhdHVzIGluCiAgIyAgICAgICAgICAgICAgICAgZWFjaCB1bmlxdWUgcmVnaW9uIChhcm0vY3l0b2JhbmQvZ2VuZSkgYW5kIHRoZSBkb21pbmFudAogICMgICAgICAgICAgICAgICAgIHN0YXR1cyBmb3IgdGhhdCByZWdpb24KICAKICAjIFRpZHlldmFsIGZvciB0aGVzZSBjb2x1bW5zIAogIHJlZ2lvbl9zeW0gPC0gcmxhbmc6OnN5bShyZWdpb25fdmFyaWFibGUpCiAgc3RhdHVzX3N5bSA8LSBybGFuZzo6c3ltKHN0YXR1c19jb2x1bW5fbmFtZSkKICAKICAjIEZvcm1hdCB0aGUgZGF0YSBhbmQgZ3JvdXAgaXQKICBzdGF0dXNfY291bnQgPC0gc3RhdHVzX2RmICU+JQogICAgY291bnQoISFyZWdpb25fc3ltLCAhIXN0YXR1c19zeW0pICU+JQogICAgIyBTcHJlYWQgdGhlIGRhdGEgLS0gZWFjaCByb3cgcmVwcmVzZW50cyBhIHVuaXF1ZSBjaHJvbW9zb21lIGFybQogICAgc3ByZWFkKCEhc3RhdHVzX3N5bSwgbikgJT4lCiAgICAjIFR1cm4gTkFzIGludG8gMHMKICAgIHJlcGxhY2VfbmEobGlzdCgKICAgICAgZ2FpbiA9IDAsCiAgICAgIGxvc3MgPSAwLAogICAgICBuZXV0cmFsID0gMCwKICAgICAgYW1wbGlmaWNhdGlvbiA9IDAsCiAgICAgIHVuY2FsbGFibGUgPSAwLAogICAgICB1bnN0YWJsZSA9IDAKICAgICkpICU+JQogICAgIyBHZXR0aW5nIGNvdW50cyBieSByZWdpb24gCiAgICBncm91cF9ieSghIXJlZ2lvbl9zeW0pIAogIAogICMgTGV0J3Mgc3RvcmUgdGhlIHJlZ2lvbnMgc2VwYXJhdGVseSBzbyBhcyB0byBhdm9pZCB3ZWlyZCBjb2VyY2lvbnMKICByZWdpb25fdmVjdG9yIDwtIAogICAgc3RhdHVzX2NvdW50ICU+JSAKICAgIGRwbHlyOjpwdWxsKCEhcmVnaW9uX3N5bSkKICAKICAjIENhbGN1bGF0ZSBwZXJjZW50IAogIHN0YXR1c19jb3VudCA8LSBzdGF0dXNfY291bnQgJT4lCiAgICAjIFN0b3JlIHJlZ2lvbiB2YXJpYWJsZSBjb2x1bW4gb3V0IG9mIHRoZSB3YXkgYXMgcm93bmFtZXMKICAgIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKHJlZ2lvbl92YXJpYWJsZSkgJT4lIAogICAgIyBPYnRhaW4gYSB0b3RhbCBjb3VudHMgdmFyaWFibGUgY29sdW1uCiAgICBkcGx5cjo6bXV0YXRlKHRvdGFsID0gYXBwbHkoLiwgMSwgc3VtKSkgJT4lIAogICAgIyBHZXQgdGhlIHJhdGlvIG9mIGVhY2ggc3RhdHVzIGNvdW50IHRvIHRoZSB0b3RhbAogICAgZHBseXI6Om11dGF0ZV9hdChkcGx5cjo6dmFycygtdG90YWwpLCBkcGx5cjo6ZnVucyguIC8gdG90YWwpKSAlPiUgCiAgICAjIEJyaW5nIGJhY2sgb3VyIHJlZ2lvbiB2YXJpYWJsZSBhcyBpdHMgb3duIGNvbHVtbgogICAgZHBseXI6Om11dGF0ZSghIXJlZ2lvbl92YXJpYWJsZSA6PSByZWdpb25fdmVjdG9yKQoKICAjIFRoZSBsb2dpYyBoZXJlIGlzIHRvIGRlZmluZSB0aGUgcmVnaW9uIHN0YXR1cyBiYXNlZCBvbiB0aGUgbWFqb3JpdHkgb2YgY2FsbHMKICAjIGluIHRoZSByZWdpb24gLS0gaWYgdGhlIG51bWJlciBvZiBjYWxscyBmb3IgYSBzcGVjaWZpYyBzdGF0dXMgaXMgCiAgIyByZXNwb25zaWJsZSBmb3IgbW9yZSB0aGFuIGBwZXJjZW50X3RocmVzaG9sZGAgdmFsdWUgb2YgdGhlIHRvdGFsIGNhbGxzIGluIHRoYXQKICAjIHJlZ2lvbiwgdGhlbiBpdCBpcyB1c2VkIHRvIGRlZmluZSB0aGUgcmVnaW9uJ3Mgc3RhdHVzIChleGNlcHRpb24gaXMgZm9yIHRoZSAKICAjIHVuY2FsbGFibGUgc3RhdHVzIHdoZXJlIHdlIGRlZmluZSB0aGUgcmVnaW9uIGFzIHVuY2FsbGFibGUgd2hlbiBtb3JlIHRoYW4gdGhlCiAgIyBgdW5jYWxsYWJsZV90aHJlc2hvbGRgIG9mIHRoZSBjYWxscyBpbiB0aGF0IHJlZ2lvbiBhcmUgdW5jYWxsYWJsZSkKICBpZiAoKHJlZ2lvbl92YXJpYWJsZSA9PSAiY2hyb21vc29tZV9hcm0iKSB8IChyZWdpb25fdmFyaWFibGUgPT0gImdlbmVfc3ltYm9sIikpIHsKICAgIHN0YXR1c19jb3VudCA8LSBzdGF0dXNfY291bnQgJT4lCiAgICAgIG11dGF0ZSgKICAgICAgICBkb21pbmFudF9zdGF0dXMgPSBjYXNlX3doZW4oCiAgICAgICAgICBnYWluID4gcGVyY2VudF90aHJlc2hvbGQgfiAiZ2FpbiIsCiAgICAgICAgICBsb3NzID4gcGVyY2VudF90aHJlc2hvbGQgfiAibG9zcyIsCiAgICAgICAgICBhbXBsaWZpY2F0aW9uID4gcGVyY2VudF90aHJlc2hvbGQgfiAiYW1wbGlmaWNhdGlvbiIsCiAgICAgICAgICBUUlVFIH4gIm5ldXRyYWwiCiAgICAgICAgKQogICAgICApCiAgfSBlbHNlIGlmIChyZWdpb25fdmFyaWFibGUgPT0gImN5dG9iYW5kIikgewogICAgc3RhdHVzX2NvdW50IDwtIHN0YXR1c19jb3VudCAlPiUKICAgICAgbXV0YXRlKAogICAgICAgIGRvbWluYW50X3N0YXR1cyA9IGNhc2Vfd2hlbigKICAgICAgICAgIHVuY2FsbGFibGUgPiB1bmNhbGxhYmxlX3RocmVzaG9sZCB+ICJ1bmNhbGxhYmxlIiwKICAgICAgICAgIGdhaW4gPiBwZXJjZW50X3RocmVzaG9sZH4gImdhaW4iLAogICAgICAgICAgbG9zcyA+IHBlcmNlbnRfdGhyZXNob2xkIH4gImxvc3MiLAogICAgICAgICAgbmV1dHJhbCA+IHBlcmNlbnRfdGhyZXNob2xkIH4gIm5ldXRyYWwiLAogICAgICAgICAgVFJVRSB+ICJ1bnN0YWJsZSIKICAgICAgICApCiAgICAgICkKICB9CgogIHJldHVybihzdGF0dXNfY291bnQpCn0KYGBgCgpgYGB7cn0KcGxvdF9kb21pbmFudF9zdGF0dXNfY2FsbHMgPC0gZnVuY3Rpb24oc3RhdHVzX2NvdW50X2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWdpb25fdmFyaWFibGUpIHsKICAjIEdpdmVuIGEgZGF0YS5mcmFtZSB3aXRoIHRoZSBwZXJjZW50YWdlIHZhbHVlcyBmb3IgZWFjaCByZWdpb24gYW5kIHRoZQogICMgZG9taW5hbnQgc3RhdHVzIGZvciB0aGF0IHJlZ2lvbiwgcGxvdCB0aGUgZG9taW5hbnQgc3RhdHVzIGNhbGwgb24gdGhlCiAgIyB4LWF4aXMgd2l0aCB0aGUgcGVyY2VudGFnZSB2YWx1ZXMgb24gdGhlIHktYXhpcy4KICAjCiAgIyBBcmdzOgogICMgICBzdGF0dXNfY291bnRfZGY6IGRhdGEuZnJhbWUgd2l0aCBwZXJjZW50YWdlIHZhbHVlcyBmb3IgZWFjaCB1bmlxdWUgc3RhdHVzCiAgIyAgICAgICAgICAgICAgICAgICAgaW4gZWFjaCB1bmlxdWUgcmVnaW9uIChhcm0vY3l0b2JhbmQvZ2VuZSkgYW5kIHRoZQogICMgICAgICAgICAgICAgICAgICAgIGRvbWluYW50IHN0YXR1cyBmb3IgdGhhdCByZWdpb24KICAjICAgcmVnaW9uX3ZhcmlhYmxlOiBzdHJpbmcgb2YgdGhlIHJlZ2lvbiAod2hpY2ggd2lsbCBhbHNvIGJlIGEgY29sdW1uIG5hbWUpCiAgIyAgICAgICAgICAgICAgICAgICAgdGhhdCB0aGUgZGF0YS5mcmFtZSBob2xkcyBwZXJjZW50YWdlIHZhbHVlcyBmb3IKICAjIFJldHVybjoKICAjICAgc3RhdHVzX3Bsb3Q6IHBsb3QgcmVwcmVzZW50aW5nIHRoZSBkb21pbmFudCBzdGF0dXMgY2FsbCBvbiB0aGUgeC1heGlzIGFuZAogICMgICAgICAgICAgICAgICAgdGhlIHBlcmNlbnRhZ2UgdmFsdWVzIG9uIHRoZSB5LWF4aXMKICAKICBzdGF0dXNfY291bnRfZGYgJT4lCiAgICAjIFJlbW92ZSB0aGUgdG90YWwgY29sdW1uIC0tIHdlIGRvbid0IHdhbnQgdG8gcGxvdCB0aGlzCiAgICBzZWxlY3QoLXRvdGFsKSAlPiUKICAgIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgICAjIFN0b3JlIHRoZSBub24tcGVyY2VudGFnZSB2YWx1ZSBjb2x1bW4gdmFyaWFibGUgYXMgcm93bmFtZXMKICAgIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKHJlZ2lvbl92YXJpYWJsZSkgJT4lCiAgICAjIEdhdGhlciB0aGUgZGF0YS5mcmFtZSB0byBoYXZlIGNvbHVtbnMgYW5kIHZhbHVlcyBpbiB0aGUgZm9ybWF0IG9mCiAgICAjIG91ciBzdGF0dXMgY2FsbCwgdGhlIHBlcmNlbnRhZ2Ugb2YgdG90YWwgY2FsbHMgdGhhdCBzdGF0dXMgY2FsbCBpcwogICAgIyByZXNwb25pc2JsZSBmb3IsIGFuZCB0aGUgZG9taW5hbnQgc3RhdHVzIGNhbGwgbWFkZSBiYXNlZCBvbiB0aGUKICAgICMgcGVyY2VudGFnZSB2YWx1ZQogICAgdGlkeXI6OmdhdGhlcihzdGF0dXMsIHBlcmNlbnQsLWRvbWluYW50X3N0YXR1cykgJT4lCiAgICAjIFBsb3Qgb3VyIGZvY2FsIHN0YXR1cyB2YWx1ZXMgb24gdGhlIHgtYXhpcyBhbmQgdGhlIHBlcmNlbnRhZ2UgdmFsdWVzCiAgICAjIG9uIHRoZSB5LWF4aXMKICAgIGdncGxvdDI6OmdncGxvdChnZ3Bsb3QyOjphZXMoeCA9IHN0YXR1cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHBlcmNlbnQpKSArCiAgICBnZ3Bsb3QyOjpnZW9tX2ppdHRlcigpICsKICAgICMgRmFjZXQgd3JhcCBhcm91bmQgZWFjaCBkb21pbmFudCBzdGF0dXMgdmFsdWUKICAgIGdncGxvdDI6OmZhY2V0X3dyYXAoIH4gZG9taW5hbnRfc3RhdHVzKQp9CmBgYAoKCiMjIyBGaWxlcyBhbmQgZGlyZWN0b3JpZXMKCmBgYHtyfQpyZXN1bHRzX2RpciA8LSAicmVzdWx0cyIKCiMgRGVmaW5lIGEgbG9naWNhbCBvYmplY3QgZm9yIHJ1bm5pbmcgaW4gQ0kKcnVubmluZ19pbl9jaSA8LSBwYXJhbXMkaXNfY2kKYGBgCgojIyMgUmVhZCBpbiBmaWxlcwoKUmVhZCBpbiBjeXRvYmFuZCBzdGF0dXMgZmlsZSBhbmQgZm9ybWF0IGl0IGZvciB3aGF0IHdlIHdpbGwgbmVlZCBpbiB0aGlzIG5vdGVib29rLiAKCmBgYHtyfQojIFJlYWQgaW4gdGhlIGZpbGUgd2l0aCBjb25zZW5zdXMgQ04gc3RhdHVzIGRhdGEgYW5kIHRoZSBVQ1NDIGN5dG9iYW5kIGRhdGEgLS0KIyBnZW5lcmF0ZWQgaW4gYDAzLWFkZC1jeXRvYmFuZC1zdGF0dXMtY29uc2Vuc3VzLlJtZGAKY29uc2Vuc3VzX3NlZ19jeXRvYmFuZF9zdGF0dXNfZGYgPC0KICByZWFkX3RzdihmaWxlLnBhdGgoInJlc3VsdHMiLCAiY29uc2Vuc3VzX3NlZ193aXRoX3Vjc2NfY3l0b2JhbmRfc3RhdHVzLnRzdi5neiIpKSAlPiUKICAjIE5lZWQgdGhpcyB0byBub3QgaGF2ZSBgY2hyYAogIG11dGF0ZShjaHIgPSBnc3ViKCJjaHIiLCAiIiwgY2hyKSwKICAgICAgICAgY3l0b2JhbmQgPSBwYXN0ZTAoY2hyLCBjeXRvYmFuZCkpICU+JQogIHNlbGVjdCgKICAgIGNocm9tb3NvbWVfYXJtLAogICAgIyBEaXN0aW5ndWlzaCB0aGlzIGRvbWluYW50IHN0YXR1cyB0aGF0IGlzIGJhc2VkIG9uIGN5dG9iYW5kcywgZnJvbSB0aGUgc3RhdHVzIAogICAgZG9taW5hbnRfY3l0b2JhbmRfc3RhdHVzID0gZG9taW5hbnRfc3RhdHVzLAogICAgY3l0b2JhbmQsCiAgICBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lECiAgKQpgYGAKClJlYWQgaW4gdGhlIGNocm9tb3NvbWUtbGV2ZWwgYW5kIGdlbmUtbGV2ZWwgZGF0YS4gCgpgYGB7cn0KIyBSZWFkIGluIHRoZSBhbm5vdGF0ZWQgQ04gZmlsZSAod2l0aG91dCB0aGUgVUNTQyBkYXRhKQpjb25zZW5zdXNfc2VnX2F1dG9zb21lc19kZiA8LQogIHJlYWRfdHN2KGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNvbnNlbnN1c19zZWdfYW5ub3RhdGVkX2NuX2F1dG9zb21lcy50c3YuZ3oiKSkKYGBgCgpKb2luaW5nIHRoZSBnZW5lLWxldmVsLCBjeXRvYmFuZC1sZXZlbCwgYW5kIGFybS1sZXZlbCBkYXRhIGludG8gb25lIGRhdGEgZnJhbWUuCgpgYGB7cn0KY29tYmluZWRfc3RhdHVzX2RmIDwtIGNvbnNlbnN1c19zZWdfYXV0b3NvbWVzX2RmICU+JQogIGlubmVyX2pvaW4oCiAgICBjb25zZW5zdXNfc2VnX2N5dG9iYW5kX3N0YXR1c19kZiwKICAgIGJ5ID0gYygiYmlvc3BlY2ltZW5faWQiID0gIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQiLCAiY3l0b2JhbmQiKQogICkKYGBgCgojIyBEZWZpbmUgbW9zdCBmb2NhbCB1bml0cwoKIyMjIERldGVybWluZSBjaHJvbW9zb21lIGFybSBzdGF0dXMKCmBgYHtyfQojIFVzZSBvdXIgY3VzdG9tIGZ1bmN0aW9uIHRvIG1ha2UgdGhlIHN0YXR1cyBjYWxscwphcm1fc3RhdHVzX2NvdW50IDwtIHN0YXR1c19tYWpvcml0eV9jYWxsZXIoCiAgY29tYmluZWRfc3RhdHVzX2RmLAogICJjaHJvbW9zb21lX2FybSIsCiAgInN0YXR1cyIsIAogIHBlcmNlbnRfdGhyZXNob2xkID0gcGVyY2VudF90aHJlc2hvbGQKKQoKIyBEaXNwbGF5IHRhYmxlCmFybV9zdGF0dXNfY291bnQgJT4lCiAgZ3JvdXBfYnkoZG9taW5hbnRfc3RhdHVzKQpgYGAKClRoZXNlIGFyZSB0aGUgY2hyb21vc29tZSBhcm1zIHRoYXQgaGF2ZSBub3QgYmVlbiBkZWZpbmVkIGFzIGdhaW4gb3IgbG9zcyAtLSB3ZSB3YW50IHRvIGRlZmluZSB0aGVpciBjeXRvYmFuZC9nZW5lLWxldmVsIHN0YXR1cwoKYGBge3J9CiMgTGV0J3MgZ2V0IGEgdmVjdG9yIG9mIHRoZSBuZXV0cmFsIGFybXMKbmV1dHJhbF9hcm1zIDwtIGFybV9zdGF0dXNfY291bnQgJT4lCiAgZmlsdGVyKGRvbWluYW50X3N0YXR1cyA9PSAibmV1dHJhbCIpICU+JSAKICBkcGx5cjo6cHVsbCgiY2hyb21vc29tZV9hcm0iKQpgYGAKCiMjIyBEZXRlcm1pbmUgY3l0b2JhbmQgc3RhdHVzCgpXZSB3YW50IHRvIGluY2x1ZGUgY3l0b2JhbmQgYW5kIGdlbmUtbGV2ZWwgY2FsbHMgZm9yIGNocm9tb3NvbWUgYXJtcyB0aGF0IGhhdmUgbm90IGJlZW4gZGVmaW5lZCBhcyBhIGdhaW4gb3IgbG9zcy4KCmBgYHtyfQojIEZpbHRlciB0aGUgYW5ub3RhdGVkIENOIGRhdGEgdG8gaW5jbHVkZSBvbmx5IG5ldXRyYWwgY2hyb21vc29tZSBhcm1zCm5ldXRyYWxfc3RhdHVzX2FybV9kZiA8LSBjb21iaW5lZF9zdGF0dXNfZGYgJT4lCiAgZmlsdGVyKGNocm9tb3NvbWVfYXJtICVpbiUgbmV1dHJhbF9hcm1zKQpgYGAKCk1ha2luZyBjeXRvYmFuZC1sZXZlbCBtYWpvcml0eSBjYWxscy4gCgpgYGB7cn0KaWYgKCEocnVubmluZ19pbl9jaSkpIHsKIyBOb3cgY291bnQgdGhlIGN5dG9iYW5kIGxldmVsIGNhbGxzIChmb3IgZWFjaCBzdGF0dXMgY2FsbCkgYW5kIGRlZmluZQojIHRoZSBjeXRvYmFuZCBhcyB0aGF0IHN0YXR1cyBpZiBtb3JlIHRoYW4gNTAlIG9mIHRoZSB0b3RhbCBjb3VudHMgYXJlCiMgZm9yIHRoYXQgcGFydGljdWxhciBzdGF0dXMKY3l0b2JhbmRfc3RhdHVzX2NvdW50IDwtIHN0YXR1c19tYWpvcml0eV9jYWxsZXIoCiAgbmV1dHJhbF9zdGF0dXNfYXJtX2RmLAogICJjeXRvYmFuZCIsCiAgImRvbWluYW50X2N5dG9iYW5kX3N0YXR1cyIsCiAgcGVyY2VudF90aHJlc2hvbGQgPSBwZXJjZW50X3RocmVzaG9sZAopCgojIERpc3BsYXkgdGFibGUKY3l0b2JhbmRfc3RhdHVzX2NvdW50Cn0KYGBgCgojIyMgRGV0ZXJtaW5lIGdlbmUtbGV2ZWwgc3RhdHVzCgpgYGB7cn0KaWYgKCEocnVubmluZ19pbl9jaSkpIHsKICAjIFRoZXNlIGFyZSB0aGUgY3l0b2JhbmRzIHRoYXQgaGF2ZSBub3QgYmVlbiBkZWZpbmVkIGFzIGdhaW4gb3IgbG9zcyAtLQogICMgd2Ugd2FudCB0byBkZWZpbmUgdGhlaXIgZ2VuZS1sZXZlbCBzdGF0dXMKICBuZXV0cmFsX2N5dG9iYW5kcyA8LSBjeXRvYmFuZF9zdGF0dXNfY291bnQgJT4lCiAgICBmaWx0ZXIoZG9taW5hbnRfc3RhdHVzICVpbiUgYygidW5zdGFibGUiLCAibmV1dHJhbCIpKSAlPiUKICAgIGRwbHlyOjpwdWxsKCJjeXRvYmFuZCIpCgogICMgRmlsdGVyIHRoZSBhbm5vdGF0ZWQgQ04gZGF0YSB0byBpbmNsdWRlIG9ubHkgdGhlc2UgY3l0b2JhbmRzCiAgbmV1dHJhbF9zdGF0dXNfY3l0b2JhbmRfZGYgPC0gY29tYmluZWRfc3RhdHVzX2RmICU+JQogICAgZmlsdGVyKGN5dG9iYW5kICVpbiUgbmV1dHJhbF9jeXRvYmFuZHMpCiAgCiAgIyBOb3cgY291bnQgdGhlIGdlbmUtbGV2ZWwgY2FsbHMgKGZvciBlYWNoIHN0YXR1cyBjYWxsKSBhbmQgZGVmaW5lCiAgIyB0aGUgZ2VuZSBhcyB0aGF0IHN0YXR1cyBpZiBtb3JlIHRoYW4gNTAlIG9mIHRoZSB0b3RhbCBjb3VudHMgYXJlCiAgIyBmb3IgdGhhdCBwYXJ0aWN1bGFyIHN0YXR1cwogIGdlbmVfc3RhdHVzX2NvdW50IDwtIHN0YXR1c19tYWpvcml0eV9jYWxsZXIobmV1dHJhbF9zdGF0dXNfY3l0b2JhbmRfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZ2VuZV9zeW1ib2wiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0YXR1cyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVyY2VudF90aHJlc2hvbGQgPSBwZXJjZW50X3RocmVzaG9sZCkKICAKICAjIERpc3BsYXkgdGFibGUKICBnZW5lX3N0YXR1c19jb3VudAp9CmBgYAoKIyMgUGxvdCBjYWxscwoKUGxvdCB0aGUgZmluYWwgZG9taW5hbnQgc3RhdHVzIGNhbGwgb24gdGhlIHgtYXhpcyBhbmQgdGhlIHBlcmNlbnQgb2YgZWFjaCBzdGF0dXMgb24gdGhlIHktYXhpcy4KCiMjIyBQbG90IGNocm9tb3NvbWUgYXJtIHN0YXR1cyBjYWxscwoKYGBge3J9CiMgUnVuIGBwbG90X2RvbWluYW50X3N0YXR1c19jYWxsc2AgZnVuY3Rpb24gZm9yIHRoZSBjaHJvbW9zb21lIGFybSBjYWxscwpwbG90X2RvbWluYW50X3N0YXR1c19jYWxscygKICBhcm1fc3RhdHVzX2NvdW50LAogICJjaHJvbW9zb21lX2FybSIKKQpgYGAKCiMjIyBQbG90IGN5dG9iYW5kIHN0YXR1cyBjYWxscwoKYGBge3IgZmlnLndpZHRoID0gMTB9CiMgUnVuIGBwbG90X2RvbWluYW50X3N0YXR1c19jYWxsc2AgZnVuY3Rpb24gZm9yIHRoZSBjeXRvYmFuZCBjYWxscyBpZiBub3QKIyBydW5uaW5nIGluIENJCmlmICghKHJ1bm5pbmdfaW5fY2kpKSB7CiAgcGxvdF9kb21pbmFudF9zdGF0dXNfY2FsbHMoY3l0b2JhbmRfc3RhdHVzX2NvdW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjeXRvYmFuZCIpCn0KYGBgCgojIyMgUGxvdCBnZW5lIHN0YXR1cyBjYWxscwoKYGBge3J9CiMgUGxvdCBpZiBub3QgcnVubmluZyBpbiBjaXJjbGVDSQppZiAoIShydW5uaW5nX2luX2NpKSkgewogIHBsb3RfZG9taW5hbnRfc3RhdHVzX2NhbGxzKGdlbmVfc3RhdHVzX2NvdW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnZW5lX3N5bWJvbCIpCn0KYGBgCgojIyBDb21iaW5lIGFybSwgY3l0b2JhbmQsIGFuZCBnZW5lLWxldmVsIHN0YXR1cyBkYXRhCgpgYGB7cn0KIyBUaGUgbG9naWMgdmFyaWFibGUgYHJ1bm5pbmdfaW5fY2lgIGlzIG5lZWRlZCBoZXJlIGJlY2F1c2UgdGhlIENJIHRlc3RpbmcKIyBmaWxlcyBkbyBub3QgY29udGFpbiBhbnkgb2YgdGhlIGdlbmVzIGluIHRoZSBgZ2VuZV9zdGF0dXNfY291bnRgIGRhdGEuZnJhbWUKIyBnZW5lcmF0ZWQgYWJvdmUgKHdoZW4gYHJ1bm5pbmdfaW5fY2lgID09IEZBTFNFKQppZiAoIShydW5uaW5nX2luX2NpKSkgewogIGZpbmFsX2RmIDwtIGNvbnNlbnN1c19zZWdfYXV0b3NvbWVzX2RmICU+JQogICAgbXV0YXRlKGNocm9tb3NvbWVfYXJtID0gZ3N1YigiKHB8cSkuKiIsICJcXDEiLCBjeXRvYmFuZCkpICU+JQogICAgaW5uZXJfam9pbihhcm1fc3RhdHVzX2NvdW50LAogICAgICAgICAgICAgICBieSA9ICJjaHJvbW9zb21lX2FybSIpICU+JQogICAgbGVmdF9qb2luKGN5dG9iYW5kX3N0YXR1c19jb3VudCwKICAgICAgICAgICAgICBieSA9ICJjeXRvYmFuZCIsCiAgICAgICAgICAgICAgc3VmZml4ID0gYygiLmFybSIsICIuY3l0b2JhbmQiKSkgJT4lCiAgICBsZWZ0X2pvaW4oZ2VuZV9zdGF0dXNfY291bnQsCiAgICAgICAgICAgICAgYnkgPSAiZ2VuZV9zeW1ib2wiLAogICAgICAgICAgICAgIHN1ZmZpeCA9IGMoIi5hcm0iLCAiLmdlbmUiKSkgJT4lCiAgICBtdXRhdGUoCiAgICAgIGZvY2FsX2NhbGwgPSBwYXN0ZTAoCiAgICAgICAgZG9taW5hbnRfc3RhdHVzLmFybSwKICAgICAgICAiLCAiLAogICAgICAgIGRvbWluYW50X3N0YXR1cy5jeXRvYmFuZCwKICAgICAgICAiLCAiLAogICAgICAgIGRvbWluYW50X3N0YXR1cwogICAgICApLAogICAgICAjIEhlcmUgd2Ugd2FudCB0byBkZWZpbmUgdGhlIG1vc3QgZm9jYWwgY2FsbCBiYXNlZCBvbiB0aGUgYXJtLCBjeXRvYmFuZCwKICAgICAgIyBhbmQgZ2VuZSBzdGF0dXMgaW5mb3JtYXRpb24gLS0gaWYgYSBsb3NzL2dhaW4gaXMgZGVmaW5lZCBhdCB0aGUgYXJtCiAgICAgICMgbGV2ZWwgdGhlbiB0aGUgZm9jYWwgY2FsbCB3aWxsIGJlICJhcm1fbG9zcyIgb3IgImFybV9nYWluIiByZXNwZWN0aXZlbHksCiAgICAgICMgYW5kIHNvIG9uLgogICAgICBmb2NhbF9jYWxsID0gY2FzZV93aGVuKGZvY2FsX2NhbGwgPT0gImxvc3MsIE5BLCBOQSIgfiAiYXJtX2xvc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvY2FsX2NhbGwgPT0gIm5ldXRyYWwsIHVuY2FsbGFibGUsIE5BIiB+ICJ1bmNhbGxhYmxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb2NhbF9jYWxsID09ICJuZXV0cmFsLCBsb3NzLCBOQSIgfiAiY3l0b2JhbmRfbG9zcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9jYWxfY2FsbCA9PSAibmV1dHJhbCwgTkEsIE5BIiB+ICJhcm1fbmV1dHJhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9jYWxfY2FsbCA9PSAibmV1dHJhbCwgdW5zdGFibGUsIG5ldXRyYWwiIH4gImdlbmVfbmV1dHJhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9jYWxfY2FsbCA9PSAibmV1dHJhbCwgdW5zdGFibGUsIGxvc3MiIH4gImdlbmVfbG9zcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJPdGhlciIpCiAgICApCn0gZWxzZSB7CiAgZmluYWxfZGYgPC0gY29uc2Vuc3VzX3NlZ19hdXRvc29tZXNfZGYgJT4lCiAgICBtdXRhdGUoY2hyb21vc29tZV9hcm0gPSBnc3ViKCIocHxxKS4qIiwgIlxcMSIsIGN5dG9iYW5kKSkgJT4lCiAgICBpbm5lcl9qb2luKGFybV9zdGF0dXNfY291bnQsIGJ5ID0gImNocm9tb3NvbWVfYXJtIikgJT4lCiAgICBtdXRhdGUoZm9jYWxfY2FsbCA9IGRvbWluYW50X3N0YXR1cykKfQoKIyBEaXNwbGF5IGZpbmFsIHRhYmxlCmZpbmFsX2RmICU+JQogIGFycmFuZ2UoZm9jYWxfY2FsbCkgJT4lCiAgZ3JvdXBfYnkoYmlvc3BlY2ltZW5faWQsIGZvY2FsX2NhbGwpCmBgYAoKIyMgVHJhbnNmb3JtIGRhdGEgaW50byBsb25nIGZvcm1hdAoKIyMjIFRyYW5mb3JtIHRoZSBjaHJvbW9zb21lIGFybSBzdGF0dXMgZGF0YQoKYGBge3J9CmlmICghKHJ1bm5pbmdfaW5fY2kpKSB7CiAgZmluYWxfYXJtX3N0YXR1c19kZiA8LSBmaW5hbF9kZiAlPiUKICAgICMgRmlsdGVyIHRvIG9ubHkgbm9uLW5ldXRyYWwgY2hyb21vc29tZSBhcm1zCiAgICBmaWx0ZXIoZG9taW5hbnRfc3RhdHVzLmFybSAhPSAibmV1dHJhbCIpICU+JQogICAgc2VsZWN0KAogICAgICBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEID0gYmlvc3BlY2ltZW5faWQsCiAgICAgIHJlZ2lvbiA9IGNocm9tb3NvbWVfYXJtLAogICAgICBzdGF0dXMgPSBkb21pbmFudF9zdGF0dXMuYXJtCiAgICApICU+JQogICAgZGlzdGluY3QoKSAlPiUKICAgIG11dGF0ZShyZWdpb25fdHlwZSA9ICJjaHJvbW9zb21lX2FybSIpCn0gZWxzZSB7CiAgZmluYWxfYXJtX3N0YXR1c19kZiA8LSBmaW5hbF9kZiAlPiUKICAgICMgRmlsdGVyIHRvIG9ubHkgbm9uLW5ldXRyYWwgY2hyb21vc29tZSBhcm1zCiAgICBmaWx0ZXIoZG9taW5hbnRfc3RhdHVzICE9ICJuZXV0cmFsIikgJT4lCiAgICBzZWxlY3QoCiAgICAgIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQgPSBiaW9zcGVjaW1lbl9pZCwKICAgICAgcmVnaW9uID0gY2hyb21vc29tZV9hcm0sCiAgICAgIHN0YXR1cyA9IGRvbWluYW50X3N0YXR1cwogICAgKSAlPiUKICAgIGRpc3RpbmN0KCkgJT4lCiAgICBtdXRhdGUocmVnaW9uX3R5cGUgPSAiY2hyb21vc29tZV9hcm0iKQp9CmBgYAoKIyMjIFRyYW5zZm9ybSB0aGUgY3l0b2JhbmQgc3RhdHVzIGRhdGEKCmBgYHtyfQppZiAoIShydW5uaW5nX2luX2NpKSkgewogIGZpbmFsX2N5dG9iYW5kX3N0YXR1c19kZiA8LSBmaW5hbF9kZiAlPiUKICAgICMgRmlsdGVyIHRvIG9ubHkgbmV1dHJhbCBjaHJvbW9zb21lIGFybXMgYW5kIGN5dG9iYW5kcwogICAgIyB0aGF0IGFyZSBub3QgTkEKICAgIGZpbHRlcihkb21pbmFudF9zdGF0dXMuYXJtID09ICJuZXV0cmFsIiwKICAgICAgICAgICBkb21pbmFudF9zdGF0dXMuY3l0b2JhbmQgIT0gIk5BIikgJT4lCiAgICBzZWxlY3QoCiAgICAgIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQgPSBiaW9zcGVjaW1lbl9pZCwKICAgICAgcmVnaW9uID0gY3l0b2JhbmQsCiAgICAgIHN0YXR1cyA9IGRvbWluYW50X3N0YXR1cy5jeXRvYmFuZAogICAgKSAlPiUKICAgIGRpc3RpbmN0KCkgJT4lCiAgICBtdXRhdGUocmVnaW9uX3R5cGUgPSAiY3l0b2JhbmQiKQp9CmBgYAoKIyMjIFRyYW5zZm9ybSB0aGUgZ2VuZS1sZXZlbCBzdGF0dXMgZGF0YQoKYGBge3J9CmlmICghKHJ1bm5pbmdfaW5fY2kpKSB7CiAgZmluYWxfZ2VuZV9zdGF0dXNfZGYgPC0gZmluYWxfZGYgJT4lCiAgICAjIEZpbHRlciB0byBvbmx5IG5ldXRyYWwgY2hyb21vc29tZSBhcm1zIGFuZCBjeXRvYmFuZAogICAgIyB0aGF0IGFyZSBOQQogICAgZmlsdGVyKGRvbWluYW50X3N0YXR1cy5hcm0gPT0gIm5ldXRyYWwiLAogICAgICAgICAgIGRvbWluYW50X3N0YXR1cy5jeXRvYmFuZCA9PSAiTkEiKSAlPiUKICAgIHNlbGVjdCgKICAgICAgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCA9IGJpb3NwZWNpbWVuX2lkLAogICAgICByZWdpb24gPSBnZW5lX3N5bWJvbCwKICAgICAgc3RhdHVzID0gZG9taW5hbnRfc3RhdHVzCiAgICApICU+JQogICAgZGlzdGluY3QoKSAlPiUKICAgIG11dGF0ZShyZWdpb25fdHlwZSA9ICJnZW5lX3N5bWJvbCIpCn0KYGBgCgojIyMgQ29tYmluZSBzdGF0dXMgZGF0YSBmb3IgYWxsIHJlZ2lvbnMKCmBgYHtyfQppZiAoIShydW5uaW5nX2luX2NpKSkgewogICMgQmluZCB0aGUgcm93cyBvZiBlYWNoIHJlZ2lvbidzIGRhdGEuZnJhbWUKICBmaW5hbF9sb25nX3N0YXR1c19kZiA8LSBiaW5kX3Jvd3MoZmluYWxfYXJtX3N0YXR1c19kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmluYWxfY3l0b2JhbmRfc3RhdHVzX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaW5hbF9nZW5lX3N0YXR1c19kZikgJT4lCiAgICBmaWx0ZXIoc3RhdHVzICE9ICJ1bmNhbGxhYmxlIikKfSBlbHNlIHsKICBmaW5hbF9sb25nX3N0YXR1c19kZiA8LSBmaW5hbF9hcm1fc3RhdHVzX2RmICU+JQogICAgZmlsdGVyKHN0YXR1cyAhPSAidW5jYWxsYWJsZSIpCn0KCiMgV3JpdGUgZmluYWwgbG9uZyBzdGF0dXMgdGFibGUgdG8gZmlsZQp3cml0ZV90c3YoZmluYWxfbG9uZ19zdGF0dXNfZGYsIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNvbnNlbnN1c19zZWdfbW9zdF9mb2NhbF9jbl9zdGF0dXMudHN2Lmd6IikpCgojIERpc3BsYXkgZmluYWwgbG9uZyBzdGF0dXMgdGFibGUKZmluYWxfbG9uZ19zdGF0dXNfZGYKYGBgCgoqKk5vdGU6KiogVGhlcmUgYXJlIG5vIGdlbmUgcmVnaW9uIHN0YXR1cyBkYXRhIGluIHRoZSBmaW5hbCBsb25nIHN0YXR1cyBkYXRhLmZyYW1lIHVzaW5nIHRoZSBsb2dpYyBhYm92ZS4KVGhlcmUgYXJlIGFsc28gbm8gZ2FpbiBzdGF0dXMgY2FsbHMgaW4gdGhpcyBmaW5hbCBkYXRhLmZyYW1lLgoKIyMjIFNwcmVhZCBzdGF0dXMgZGF0YQoKYGBge3J9CmZpbmFsX3dpZGVfc3RhdHVzX2RmIDwtIGZpbmFsX2xvbmdfc3RhdHVzX2RmICU+JQogIHRpZHlyOjpzcHJlYWQoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgc3RhdHVzKQoKIyBEaXNwbGF5IHN0YXR1cyBkYXRhIHNwcmVhZCBhY3Jvc3Mgc2FtcGxlcyBpbiB3aWRlIGZvcm1hdCAtLSBlYWNoIHJvdyBpcyBhCiMgdW5pcXVlIHJlZ2lvbgpmaW5hbF93aWRlX3N0YXR1c19kZgpgYGAKCgojIyBTZXNzaW9uIEluZm8KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=